﻿using PmSoft.Core.Domain.Entities;

namespace PmSoft.Data.Abstractions;

/// <summary>
/// 分页数据处理和转换的扩展方法集合
/// </summary>
public static class PagedDataExtensions
{
	/// <summary>
	/// 将新的结果集合包裹到原始分页信息中，生成一个新的分页列表
	/// </summary>
	/// <typeparam name="TSource">原始分页数据的元素类型</typeparam>
	/// <typeparam name="TResult">新结果集合的元素类型</typeparam>
	/// <param name="source">原始分页数据，提供分页元数据（页码、页大小等）</param>
	/// <param name="results">新的结果集合</param>
	/// <returns>新的分页列表</returns>
	public static IPagedList<TResult> WrapToPagedList<TSource, TResult>(
		this IPagedList<TSource> source,
		IEnumerable<TResult> results)
	{
		// 显式空值检查（即使底层构造函数可能已包含）
		if (results == null)
			throw new ArgumentNullException(nameof(results));
		// 构造新的分页结果，继承原始分页信息
		return new PagedList<TResult>(results)
		{
			PageIndex = source.PageIndex,
			PageSize = source.PageSize,
			Total = source.Total,
			QueryDuration = source.QueryDuration
		};
	}

	/// <summary>
	/// 将分页数据转换为 DTO 并支持链式加载多个关联实体
	/// </summary>
	/// <typeparam name="TSource">源实体类型</typeparam>
	/// <typeparam name="TResult">目标 DTO 类型</typeparam>
	/// <param name="pagedSource">原始分页数据</param>
	/// <param name="dtoSelector">从源实体到 DTO 的转换函数</param>
	/// <returns>转换后的分页 DTO 列表</returns>
	public static IPagedList<TResult> ToPagedDto<TSource, TResult>(
		this IPagedList<TSource> pagedSource,
		Func<TSource, TResult> dtoSelector)
	{
		var dtos = pagedSource.Items.Select(dtoSelector).ToList();
		return pagedSource.WrapToPagedList(dtos);
	}

	/// <summary>
	/// 将分页数据转换为 DTO 并支持链式加载多个关联实体
	/// </summary>
	/// <typeparam name="TSource">源实体类型</typeparam>
	/// <typeparam name="TResult">目标 DTO 类型</typeparam>
	/// <param name="pagedSource">原始分页数据</param>
	/// <param name="asyncDtoMapper">从源实体到 DTO 的转换函数</param>
	/// <returns>转换后的分页 DTO 列表</returns>
	public static async Task<IPagedList<TResult>> ToPagedDtoAsync<TSource, TResult>(
		this IPagedList<TSource> pagedSource,
	 Func<TSource, Task<TResult>> asyncDtoMapper)
	{
		var dtos = new List<TResult>();
		foreach (var item in pagedSource.Items)
		{
			var dto = await asyncDtoMapper(item); // 等待每个异步转换完成
			dtos.Add(dto);
		}
		return pagedSource.WrapToPagedList(dtos);
	}

	/// <summary>
	/// 链式加载单个关联实体到分页数据
	/// </summary>
	/// <typeparam name="TResult">分页数据中的元素类型</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型</typeparam>
	/// <param name="paged">分页数据</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数</param>
	/// <param name="attachAction">将关联实体附加到结果的动作</param>
	/// <returns>包含关联实体的新分页列表</returns>
	public static async Task<IPagedList<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this IPagedList<TResult> paged,
		Func<TResult, TRelatedKey?> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var results = await paged.Items.WithRelatedAsync(foreignKeySelector, loadRelatedEntities, attachAction);
		return paged.WrapToPagedList(results);
	}

	/// <summary>
	/// 链式加载单个关联实体到分页数据
	/// </summary>
	/// <typeparam name="TResult">分页数据中的元素类型</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型</typeparam>
	/// <param name="paged">分页数据</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数</param>
	/// <param name="attachAction">将关联实体附加到结果的动作</param>
	/// <returns>包含关联实体的新分页列表</returns>
	public static async Task<IPagedList<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this IPagedList<TResult> paged,
		Func<TResult, TRelatedKey> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var results = await paged.Items.WithRelatedAsync(foreignKeySelector, loadRelatedEntities, attachAction);
		return paged.WrapToPagedList(results);
	}

	//// <summary>
	/// 链式加载单个关联实体到分页数据（支持异步任务）。
	/// </summary>
	/// <typeparam name="TResult">分页数据中的元素类型。</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型。</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型。</typeparam>
	/// <param name="pagedTask">异步分页数据任务。</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数。</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数。</param>
	/// <param name="attachAction">将关联实体附加到结果的动作。</param>
	/// <returns>包含关联实体的新分页列表。</returns>
	public static async Task<IPagedList<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this Task<IPagedList<TResult>> pagedTask,
		Func<TResult, TRelatedKey?> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var paged = await pagedTask;
		return await paged.WithRelatedAsync(foreignKeySelector, loadRelatedEntities, attachAction);
	}

	//// <summary>
	/// 链式加载单个关联实体到分页数据（支持异步任务）。
	/// </summary>
	/// <typeparam name="TResult">分页数据中的元素类型。</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型。</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型。</typeparam>
	/// <param name="pagedTask">异步分页数据任务。</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数。</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数。</param>
	/// <param name="attachAction">将关联实体附加到结果的动作。</param>
	/// <returns>包含关联实体的新分页列表。</returns>
	public static async Task<IPagedList<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this Task<IPagedList<TResult>> pagedTask,
		Func<TResult, TRelatedKey> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var paged = await pagedTask;
		return await paged.WithRelatedAsync(foreignKeySelector, loadRelatedEntities, attachAction);
	}


	/// <summary>
	/// 链式加载单个关联实体到结果集合
	/// </summary>
	/// <typeparam name="TResult">结果集合中的元素类型</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型</typeparam>
	/// <param name="resultsTask">结果集合数据任务</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数</param>
	/// <param name="attachAction">将关联实体附加到结果的动作</param>
	/// <returns>包含关联实体的新结果集合</returns>
	public static async Task<IEnumerable<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this Task<IEnumerable<TResult>> resultsTask,
		Func<TResult, TRelatedKey?> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var results = await resultsTask;
		return await results.WithRelatedAsync(foreignKeySelector, loadRelatedEntities, attachAction);
	}

	/// <summary>
	/// 链式加载单个关联实体到结果集合
	/// </summary>
	/// <typeparam name="TResult">结果集合中的元素类型</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型</typeparam>
	/// <param name="resultsTask">结果集合数据任务</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数</param>
	/// <param name="attachAction">将关联实体附加到结果的动作</param>
	/// <returns>包含关联实体的新结果集合</returns>
	public static async Task<IEnumerable<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this Task<IEnumerable<TResult>> resultsTask,
		Func<TResult, TRelatedKey> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var results = await resultsTask;
		return await results.WithRelatedAsync(foreignKeySelector, loadRelatedEntities, attachAction);
	}

	/// <summary>
	/// 链式加载单个关联实体到结果集合
	/// </summary>
	/// <typeparam name="TResult">结果集合中的元素类型</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型</typeparam>
	/// <param name="results">结果集合</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数</param>
	/// <param name="attachAction">将关联实体附加到结果的动作</param>
	/// <returns>包含关联实体的新结果集合</returns>
	public static async Task<IEnumerable<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this IEnumerable<TResult> results,
		Func<TResult, TRelatedKey?> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var itemList = results.ToList();

		var relatedKeys = itemList.Select(foreignKeySelector).Where(m => m != null).Distinct()
			.Cast<TRelatedKey>()
			.ToList();
		var relatedEntities = await loadRelatedEntities(relatedKeys);
		var relatedDict = relatedEntities.ToDictionary(e => e.Id);

		foreach (var item in itemList)
		{
			TRelatedKey? relatedKey = foreignKeySelector(item);
			if (relatedKey != null && relatedDict.TryGetValue(relatedKey.Value, out var related))
			{
				attachAction(item, related);
			}
		}

		return itemList;
	}

	/// <summary>
	/// 链式加载单个关联实体到结果集合
	/// </summary>
	/// <typeparam name="TResult">结果集合中的元素类型</typeparam>
	/// <typeparam name="TRelatedEntity">关联实体类型</typeparam>
	/// <typeparam name="TRelatedKey">关联实体的键类型</typeparam>
	/// <param name="results">结果集合</param>
	/// <param name="foreignKeySelector">从结果中提取外键的函数</param>
	/// <param name="loadRelatedEntities">加载关联实体的异步函数</param>
	/// <param name="attachAction">将关联实体附加到结果的动作</param>
	/// <returns>包含关联实体的新结果集合</returns>
	public static async Task<IEnumerable<TResult>> WithRelatedAsync<TResult, TRelatedEntity, TRelatedKey>(
		this IEnumerable<TResult> results,
		Func<TResult, TRelatedKey> foreignKeySelector,
		Func<IEnumerable<TRelatedKey>, Task<IEnumerable<TRelatedEntity>>> loadRelatedEntities,
		Action<TResult, TRelatedEntity> attachAction)
		where TRelatedEntity : class, IEntity<TRelatedKey>, new()
		where TRelatedKey : struct
	{
		var itemList = results.ToList();

		var relatedKeys = itemList.Select(foreignKeySelector).Distinct()
			.Cast<TRelatedKey>()
			.ToList();
		var relatedEntities = await loadRelatedEntities(relatedKeys);
		var relatedDict = relatedEntities.ToDictionary(e => e.Id);

		foreach (var item in itemList)
		{
			TRelatedKey? relatedKey = foreignKeySelector(item);
			if (relatedKey != null && relatedDict.TryGetValue(relatedKey.Value, out var related))
			{
				attachAction(item, related);
			}
		}

		return itemList;
	}
}